[ハイパフォーマンスかつ最小コストで推論を実現] Amazon EC2 Inf1 x AWS Neuron SDK [使ってみた] – 機械学習 on AWS Advent Calendar 2019
こんにちは、Mr.Moです。
当エントリは『機械学習 on AWS Advent Calendar 2019』の18日目のエントリです。
- クラスメソッド 機械学習 on AWS Advent Calendar 2019 - Qiita
- クラスメソッド 機械学習 on AWS Advent Calendar 2019 | シリーズ | Developers.IO
機械学習の推論に特化したサービスがre:invent 2019 で発表されましたのでさっそく使ってみたいと思います。
Amazon EC2 Inf1とは
Inf1 インスタンスでは、AWS が設計、開発した、ハイパフォーマンス機械学習推論チップ、AWS Inferentia チップを 16 基まで利用できます。さらに、推論チップを最新のカスタム第 2 世代インテル® Xeon® スケーラブルプロセッサおよび最大 100 Gbps のネットワークと組み合わせることにより、ハイスループットの推論を可能にしました。 Amazon EC2 G4 インスタンスと比較しても、推論作業あたり 3 倍のスループット、40% のコスト削減を実現 画像認識、音声認識、自然言語処理、パーソナライズ、不正検知といった大規模な機械学習推論アプリケーションを、クラウドで最小のコストで実行することができます。
また、AWS Inferentiaチップを使用して機械学習推論を実行するためのソフトウェア開発キット「AWS Neuron SDK」も用意されています。
さっそく試してみる
下記のドキュメントを参考に進めていきます。
https://github.com/aws/aws-neuron-sdk/blob/master/docs/tensorflow-neuron/tutorial-compile-infer.md
Amazon EC2 Inf1 インスタンス作成
現時点では、バージニア(us-east-1)、オレゴン(us-west-2)の2つのリージョンで利用可能ですね。
下記のURLからEC2のインスタンス作成画面を開きます。
https://console.aws.amazon.com/ec2/home?region=us-east-1#LaunchInstanceWizard:
今回はDeep Learning AMIを選択したいと思います。
Deep Learning AMIは似たものが他にもありますが、 Neuronがプリインストールされている方を選びます。
次にインスタンスタイプを選択します、ここでは「inf1.6xlarge」を選択しました。あとはEC2コンソール画面の指示に従ってインスタンスの作成までを行います。
機械学習のモデルをNeuron実行可能ファイル形式(NEFF)にコンパイルする
立ち上がったEC2サーバーにSSHでログインして、既にTensorFlow-Neuron環境の環境が用意されているので source activate aws_neuron_tensorflow_p36
のコマンドを打ってActiveの状態にします。
- compile_resnet50.py
ここでは学習済みの「ResNet50」のモデルを使用します。このモデルをNeuron実行可能ファイル形式(NEFF)にコンパイルします。 下記のプログラムをcompile_resnet50.pyというファイル名で保存して
import os import time import shutil import tensorflow as tf import tensorflow.neuron as tfn import tensorflow.compat.v1.keras as keras from tensorflow.keras.applications.resnet50 import ResNet50 from tensorflow.keras.applications.resnet50 import preprocess_input # Create a workspace WORKSPACE = './ws_resnet50' os.makedirs(WORKSPACE, exist_ok=True) # Prepare export directory (old one removed) model_dir = os.path.join(WORKSPACE, 'resnet50') compiled_model_dir = os.path.join(WORKSPACE, 'resnet50_neuron') shutil.rmtree(model_dir, ignore_errors=True) shutil.rmtree(compiled_model_dir, ignore_errors=True) # Instantiate Keras ResNet50 model keras.backend.set_learning_phase(0) keras.backend.set_image_data_format('channels_last') model = ResNet50(weights='imagenet') # Export SavedModel tf.saved_model.simple_save( session = keras.backend.get_session(), export_dir = model_dir, inputs = {'input': model.inputs[0]}, outputs = {'output': model.outputs[0]}) # Compile using Neuron tfn.saved_model.compile(model_dir, compiled_model_dir) # Prepare SavedModel for uploading to Inf1 instance shutil.make_archive('./resnet50_neuron', 'zip', WORKSPACE, 'resnet50_neuron')
下記のコマンドを実行します。
$ python compile_resnet50.py
下記のようにコンパイルした成果物ができていますね。
推論の実行
それでは早速、さきほどコンパイルしたモデルを使用して推論を実行してみます。
まずはコンパイルしたモデルのzipファイルを解凍し、サンプルのイメージをダウンロードします。
$ unzip -o resnet50_neuron.zip $ curl -O https://raw.githubusercontent.com/awslabs/mxnet-model-server/master/docs/images/kitten_small.jpg $ pip install pillow # Necessary for loading images
ちなみに、サンプルのイメージは下記の猫の画像でしたー(私、下記の猫の種類全くわかりません...)
- infer_resnet50.py
それでは先ほどのダウンロードしたサンプルの猫の画像を推論してます。 下記のプログラムをinfer_resnet50.pyというファイル名で保存して
import os import time import numpy as np import tensorflow as tf from tensorflow.keras.preprocessing import image from tensorflow.keras.applications import resnet50 tf.keras.backend.set_image_data_format('channels_last') # Create input from image img_sgl = image.load_img('kitten_small.jpg', target_size=(224, 224)) img_arr = image.img_to_array(img_sgl) img_arr2 = np.expand_dims(img_arr, axis=0) img_arr3 = resnet50.preprocess_input(img_arr2) # Load model COMPILED_MODEL_DIR = './resnet50_neuron/' predictor_inferentia = tf.contrib.predictor.from_saved_model(COMPILED_MODEL_DIR) # Run inference model_feed_dict={'input': img_arr3} infa_rslts = predictor_inferentia(model_feed_dict); # Display results print(resnet50.decode_predictions(infa_rslts["output"], top=5)[0])
下記のコマンドを実行します。
$ python infer_resnet50.py
実行結果として下記の推論結果が返ってきました!
[('n02123045', 'tabby', 0.6918919), ('n02127052', 'lynx', 0.12770271), ('n02123159', 'tiger_cat', 0.08277027), ('n02124075', 'Egyptian_cat', 0.06418919), ('n02128757', 'snow_leopard', 0.009290541)]
TensorFlow Servingによる推論
TensorFlow Servingを用いたサンプルコードも用意されていたのでついでに実施していこうと思います!
まず下記のコマンドを実行してみます。
$ tensorflow_model_server_neuron \ --model_name=resnet50 \ --model_base_path=/home/ec2-user/resnet50_neuron \ --port=8500
すると下記のメッセージが出続けて上手くいかず。。
W tensorflow_serving/sources/storage_path/file_system_storage_path_source.cc:267] No versions of servable resnet50 found under base path /home/ec2-user/resnet50_neuron
しかし、こちらの記事に解決方法が記載されていました!ありがたや〜 下記のコマンドを実行してバージョンフォルダを作りましょう!
$ cd resnet50_neuron $ pwd /home/ec2-user/resnet50_neuron $ mkdir 1 $ mv * 1
再度、さきほどのコマンドを実行します!
$ tensorflow_model_server_neuron \ --model_name=resnet50 \ --model_base_path=/home/ec2-user/resnet50_neuron \ --port=8500
下記のメッセージが出て上手くいったようです!
I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: resnet50 version: 1} I tensorflow_serving/model_servers/server.cc:353] Running gRPC ModelServer at 0.0.0.0:8500 ...
- tfserving_resnet50.py
あとはターミナルをもう1つ起動して下記のプログラムを実行します。
import numpy as np import grpc import tensorflow as tf from tensorflow.keras.preprocessing import image from tensorflow.keras.applications.resnet50 import preprocess_input from tensorflow.keras.applications.resnet50 import decode_predictions from tensorflow_serving.apis import predict_pb2 from tensorflow_serving.apis import prediction_service_pb2_grpc if __name__ == '__main__': channel = grpc.insecure_channel('localhost:8500') stub = prediction_service_pb2_grpc.PredictionServiceStub(channel) img_file = tf.keras.utils.get_file( "./kitten_small.jpg", "https://raw.githubusercontent.com/awslabs/mxnet-model-server/master/docs/images/kitten_small.jpg") img = image.load_img(img_file, target_size=(224, 224)) img_array = preprocess_input(image.img_to_array(img)[None, ...]) request = predict_pb2.PredictRequest() request.model_spec.name = 'resnet50' request.inputs['input'].CopyFrom( tf.contrib.util.make_tensor_proto(img_array, shape=img_array.shape)) result = stub.Predict(request) prediction = tf.make_ndarray(result.outputs['output']) print(decode_predictions(prediction))
下記のコマンドを実行します。
$ python tfserving_resnet50.py
見事、実行結果として下記の推論結果が返ってきました!
[[('n02123045', 'tabby', 0.6918919), ('n02127052', 'lynx', 0.12770271), ('n02123159', 'tiger_cat', 0.08277027), ('n02124075', 'Egyptian_cat', 0.06418919), ('n02128757', 'snow_leopard', 0.009290541)]]
まとめ
AWS Neuron SDKがあるので既存のコードを数行変えるだけで導入できそうなのが良いですね。 今後はSageMakerやPyTorchへの対応も予定されているということで、機械学習を利用しているケースで今後多くの活用が期待されますね。